import 'dart:async';

import 'package:built_collection/built_collection.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/collection_list_item/collection_list_item.dart';
import 'package:mobile_app.core/core/domain/repositories/learning_content_repository/repository.dart';
import 'package:mobile_app.home.core/core/domain/helpers/apply_collection_list_item_update/helper.dart';
import 'package:mobile_app.home.core/core/domain/helpers/find_collection/helper.dart';
import 'package:mobile_app.home.core/core/domain/helpers/is_collection_selection_effective/helper.dart';

part 'failure.dart';

part 'result.dart';

abstract class ToggleCollectionSelection {
  Either<RestrictedCollectionSelectionTogglingFailure, CollectionSelectionTogglingResult> call({
    required String collectionId,
    required BuiltList<CollectionListItem> collectionListItems,
    required BuiltSet<String> permittedCollectionListItemIds,
    required BuiltSet<String> effectivelySelectedCollectionIds,
  });
}

class ToggleCollectionSelectionImpl implements ToggleCollectionSelection {
  const ToggleCollectionSelectionImpl({
    required FindCollection findCollection,
    required ApplyCollectionListItemUpdate applyCollectionListItemUpdate,
    required IsCollectionSelectionEffective isCollectionSelectionEffective,
    required LearningContentRepository learningContentRepository,
  })  : _findCollection = findCollection,
        _applyCollectionListItemUpdate = applyCollectionListItemUpdate,
        _isCollectionSelectionEffective = isCollectionSelectionEffective,
        _learningContentRepository = learningContentRepository;

  final FindCollection _findCollection;
  final ApplyCollectionListItemUpdate _applyCollectionListItemUpdate;
  final IsCollectionSelectionEffective _isCollectionSelectionEffective;

  final LearningContentRepository _learningContentRepository;

  @override
  Either<RestrictedCollectionSelectionTogglingFailure, CollectionSelectionTogglingResult> call({
    required String collectionId,
    required BuiltList<CollectionListItem> collectionListItems,
    required BuiltSet<String> permittedCollectionListItemIds,
    required BuiltSet<String> effectivelySelectedCollectionIds,
  }) {
    final collectionPermitted = permittedCollectionListItemIds.contains(collectionId);

    if (!collectionPermitted) {
      const failure = RestrictedCollectionSelectionTogglingFailure();
      return left(failure);
    }

    final collection = _findCollection(
      collectionId: collectionId,
      collectionListItems: collectionListItems,
    )!;

    final updatedCollection = collection.copyWith(
      selected: () => !collection.selected,
    );

    final updatedCollectionListItems = _applyCollectionListItemUpdate(
      updatedCollectionListItem: updatedCollection,
      collectionListItems: collectionListItems,
    );

    final collectionSelectionEffective = _isCollectionSelectionEffective(
      collection: updatedCollection,
      collectionPermitted: collectionPermitted,
    );

    final updatedEffectivelySelectedCollectionIds =
        effectivelySelectedCollectionIds.rebuild((effectivelySelectedCollectionIds) {
      if (collectionSelectionEffective) {
        effectivelySelectedCollectionIds.add(updatedCollection.id);
      } else {
        effectivelySelectedCollectionIds.remove(updatedCollection.id);
      }
    });

    final result = CollectionSelectionTogglingResult(
      collectionListItems: updatedCollectionListItems,
      effectivelySelectedCollectionIds: updatedEffectivelySelectedCollectionIds,
    );

    unawaited(_learningContentRepository.saveCollectionListItems(
      collectionListItems: updatedCollectionListItems,
    ));

    return right(result);
  }
}
